package trabalho.das.fxml.views.uicomponents;

import entidades.Ancora;
import entidades.Familia;
import entidades.Rectangulo;
import entidades.TelhaBase;
import entidades.util.Grelha;
import entidades.util.LinhaDaGrelha;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.event.ActionEvent;
import javafx.geometry.Point2D;
import javafx.geometry.Side;
import javafx.scene.Cursor;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import maquinaestados.Estado;
import maquinaestados.EstadoAdicionarAncora;
import maquinaestados.EstadoMover;
import maquinaestados.EstadoNormal;
import maquinaestados.EstadoResize;
import maquinaestados.IEstadoVisitante;
import sistema.Sistema;
import sistema.util.KeyTelha;
import trabalho.das.fxml.views.util.ConstantesGraficas;

/**
 * Trabalho DAS - 2014/2015
 *
 * @author a21210380, a21190325, a21210392
 */
public class CanvasGrid extends ScrollPane implements IEstadoVisitante {

    public interface ControladorCanvasGrid {

        public void decorar(TelhaBase telha);

        public void criarCopia(TelhaBase telha);

        public void apagar(TelhaBase telha);

        public void criarEstiloDeTelha(TelhaBase telha);

        public void criarEstiloDeFamilia(Familia familia);

        public void inspecionar(TelhaBase telha);

        public void click(float x, float y, boolean ctr);

        public void drag(float x, float y, boolean ctr);
    }

    private final ControladorCanvasGrid controladorCanvasGrid;
    private final PaneGrelha paneGrelha;
    private final ContextMenu menuClickDireito;

    public CanvasGrid(ControladorCanvasGrid controladorCanvasGrid) {
        this.controladorCanvasGrid = controladorCanvasGrid;
        setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
        setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
        paneGrelha = new PaneGrelha();

        menuClickDireito = new ContextMenu();
        preencherMenuClickDireito();
        paneGrelha.setOnMousePressed((MouseEvent event) -> {
            if (event.isSecondaryButtonDown()) {
                menuClickDireito.show(paneGrelha, Side.TOP, event.getX(), event.getY());
                setUserData(new Point2D(event.getX(), event.getY()));
            } else {
                this.controladorCanvasGrid.click((float) event.getX(), (float) event.getY(), event.isControlDown());
            }
        });

        paneGrelha.setOnMouseDragged((MouseEvent event) -> {
            this.controladorCanvasGrid.drag((float) event.getX(), (float) event.getY(), false);
        });

        paneGrelha.setOnMouseReleased((MouseEvent event) -> {
            this.controladorCanvasGrid.drag((float) event.getX(), (float) event.getY(), true);
        });

        Sistema.getInstance().estadoProperty().addListener((ObservableValue<? extends Estado> observable, Estado oldValue, Estado newValue) -> {
            newValue.accept(this);
        });

        Grelha grelha = Sistema.getInstance().getGrelha();
        paneGrelha.setPrefSize(grelha.getLargura(), grelha.getAltura());
        paneGrelha.setStyle(ConstantesGraficas.CANVAS_GRID_STYLE);
        setContent(paneGrelha);

    }

    @Override
    public void visitante(EstadoNormal estadoNormal) {
        getScene().setCursor(Cursor.DEFAULT);
    }

    @Override
    public void visitante(EstadoMover estadoMover) {
        getScene().setCursor(Cursor.MOVE);
    }

    @Override
    public void visitante(EstadoResize estadoResize) {
        getScene().setCursor(Cursor.SE_RESIZE);
    }

    @Override
    public void visitante(EstadoAdicionarAncora estadoAdicionarAncora) {
        getScene().setCursor(Cursor.CROSSHAIR);
    }

    private TelhaBase getTelhaNoPontoClickDireito() {
        Object obj = getUserData();
        if (obj != null) {
            if (obj instanceof Point2D) {
                Point2D p = (Point2D) obj;
                return Sistema.getInstance().getTelha((float) p.getX(), (float) p.getY());
            }
        }
        return null;
    }

    private void preencherMenuClickDireito() {
        MenuItem decorar = new MenuItem("Decorar");
        decorar.setOnAction((ActionEvent e) -> {
            TelhaBase telha = getTelhaNoPontoClickDireito();
            if (telha != null) {
                controladorCanvasGrid.decorar(telha);
            }
        });

        MenuItem criarCopia = new MenuItem("Criar cópia");
        criarCopia.setOnAction((ActionEvent e) -> {
            TelhaBase telha = getTelhaNoPontoClickDireito();
            if (telha != null) {
                controladorCanvasGrid.criarCopia(telha);
            }
        });

        MenuItem apagar = new MenuItem("Apagar");
        apagar.setOnAction((ActionEvent e) -> {
            TelhaBase telha = getTelhaNoPontoClickDireito();
            if (telha != null) {
                controladorCanvasGrid.apagar(telha);
            }
        });

        MenuItem criarEstiloDeTelha = new MenuItem("Criar um estilo a partir do estilo da telha");
        criarEstiloDeTelha.setOnAction((ActionEvent e) -> {
            TelhaBase telha = getTelhaNoPontoClickDireito();
            if (telha != null) {
                controladorCanvasGrid.criarEstiloDeTelha(telha);
            }
        });

        MenuItem criarEstiloDeFamilia = new MenuItem("Criar um estilo a partir do estilo default da familia");
        criarEstiloDeFamilia.setOnAction((ActionEvent e) -> {
            TelhaBase telha = getTelhaNoPontoClickDireito();
            if (telha != null) {
                controladorCanvasGrid.criarEstiloDeFamilia(telha.getFamilia());
            }
        });

        MenuItem inspecionar = new MenuItem("Inspecionar telha");
        inspecionar.setOnAction((ActionEvent e) -> {
            TelhaBase telha = getTelhaNoPontoClickDireito();
            if (telha != null) {
                controladorCanvasGrid.inspecionar(telha);
            }
        });

        MenuItem cancelar = new MenuItem("Cancelar");
        cancelar.setOnAction((e) -> {});

        menuClickDireito.getItems().setAll(decorar, criarCopia, apagar, criarEstiloDeTelha, criarEstiloDeFamilia, inspecionar, cancelar);
    }

    private class PaneGrelha extends Pane {

        private final Map<Integer, TelhaGrafica> telhasGraficas = new HashMap<>();

        public PaneGrelha() {
            loadGrelha();

            ObservableMap<KeyTelha, TelhaBase> telhas = sistema.Sistema.getInstance().getTelhas();
            telhas.addListener(new MapChangeListener() {
                @Override
                public void onChanged(MapChangeListener.Change change) {
                    if (change.wasAdded()) {
                        bind((TelhaBase) change.getValueAdded());
                    } else {
                        unbind((TelhaBase) change.getValueRemoved());
                    }
                }
            });

            telhas.forEach((id, telha) -> bind(telha));
        }

        private void bind(TelhaBase telha) {
            if (!telhasGraficas.containsKey(telha.getId())) {
                telhasGraficas.put(telha.getId(), new TelhaGrafica(this, telha).bind());
            }
        }

        private void unbind(TelhaBase telha) {
            if (telhasGraficas.containsKey(telha.getId())) {
                telhasGraficas.remove(telha.getId()).unbind();
            }
        }

        private void loadGrelha() {
            Grelha grelha = Sistema.getInstance().getGrelha();

            grelha.getHorizontal().forEach((LinhaDaGrelha l) -> {
                MyLine line = new MyLine(grelha, l);
                getChildren().add(line);
                //debugHorizontal(l);
            });

            grelha.getVertical().forEach((LinhaDaGrelha l) -> {
                MyLine line = new MyLine(grelha, l);
                getChildren().add(line);
                //debugHorizontal(l);
            });
        }

        private void debugHorizontal(LinhaDaGrelha l) {
            Rectangulo um = l.getUm();
            Rectangle rect = new Rectangle(
                    um.getOrigem().getX(),
                    um.getOrigem().getY(),
                    um.getLargura(),
                    um.getAltura());
            rect.setFill(Color.LIGHTBLUE);
            getChildren().add(rect);

            Rectangulo um2 = l.getDois();
            Rectangle rect2 = new Rectangle(
                    um2.getOrigem().getX(),
                    um2.getOrigem().getY(),
                    um2.getLargura(),
                    um2.getAltura());
            rect2.setFill(Color.CADETBLUE);
            getChildren().add(rect2);
        }

        private void debugVertical(LinhaDaGrelha l) {
            Rectangulo um = l.getUm();
            Rectangle rect = new Rectangle(
                    um.getOrigem().getX(),
                    um.getOrigem().getY(),
                    um.getLargura(),
                    um.getAltura());
            rect.setFill(Color.LIGHTCORAL);
            getChildren().add(rect);

            Rectangulo um2 = l.getDois();
            Rectangle rect2 = new Rectangle(
                    um2.getOrigem().getX(),
                    um2.getOrigem().getY(),
                    um2.getLargura(),
                    um2.getAltura());
            rect2.setFill(Color.THISTLE);
            getChildren().add(rect2);
        }
    }
}
